/*
 * PhotonRTOS础光实时操作系统 -- mpu驱动
 *
 * Copyright (C) 2023 国科础石(重庆)软件有限公司
 *
 * 作者: Jiayuan Liang <liangjiayuan@kernelsoft.com>
 *
 * License terms: GNU General Public License (GPL) version 3
 *
 */

#include <asm/mpu.h>
#include <photon/irq.h>
#include <photon/sched.h>
#include <photon/sections.h>
#include <photon/mpu.h>
#include <photon/smp.h>

#define FIRST_ADDR                  0x00000000
#define LAST_ADDR                   0xFFFFFFFF

void enable_memory_protection()
{
    Ifx_CPU_SYSCON sysconValue;
    sysconValue.U = __mfcr(CPU_SYSCON);                 /* Get the System Configuration Register (SYSCON) value     */
    sysconValue.B.PROTEN = 1;                           /* Set the PROTEN bitfield to enable the Memory Protection  */
    __mtcr(CPU_SYSCON, sysconValue.U);                  /* Set the System Configuration Register (SYSCON)           */

#if !defined(__TASKING__)
    __isync();
#endif
}

void disable_memory_protection()
{
    Ifx_CPU_SYSCON sysconValue;
    sysconValue.U = __mfcr(CPU_SYSCON);                 /* Get the System Configuration Register (SYSCON) value     */
    sysconValue.B.PROTEN = 0;                           /* Set the PROTEN bitfield to enable the Memory Protection  */
    __mtcr(CPU_SYSCON, sysconValue.U);                  /* Set the System Configuration Register (SYSCON)           */

#if !defined(__TASKING__)
    __isync();
#endif
}

/* Function that defines a data protection range in the corresponding CPU Data Protection Range Register (DPR).
 * Data protection ranges have 8-byte granularity.
 * As a result, the lower 3 bits of any address passed to the define_data_protection_range function will be discarded.
 * After enabling the Memory Protection, access to an address 'x' will be allowed only if:
 * lowerBoundAddress <= x < upperBoundAddress
 */
void define_data_protection_range(uint32 lowerBoundAddress, uint32 upperBoundAddress, uint8 range)
{
    switch(range)
    {
        case 0: /* Data Protection Range 0 */
            __mtcr(CPU_DPR0_L, lowerBoundAddress);      /* Set the lower bound of CPU Data Protection Range 0       */
            __mtcr(CPU_DPR0_U, upperBoundAddress);      /* Set the upper bound of CPU Data Protection Range 0       */
            break;
        case 1: /* Data Protection Range 1 */
            __mtcr(CPU_DPR1_L, lowerBoundAddress);      /* Set the lower bound of CPU Data Protection Range 1       */
            __mtcr(CPU_DPR1_U, upperBoundAddress);      /* Set the upper bound of CPU Data Protection Range 1       */
            break;
        case 2: /* Data Protection Range 2 */
            __mtcr(CPU_DPR2_L, lowerBoundAddress);      /* Set the lower bound of CPU Data Protection Range 2       */
            __mtcr(CPU_DPR2_U, upperBoundAddress);      /* Set the upper bound of CPU Data Protection Range 2       */
            break;
        case 3: /* Data Protection Range 3 */
            __mtcr(CPU_DPR3_L, lowerBoundAddress);      /* Set the lower bound of CPU Data Protection Range 3       */
            __mtcr(CPU_DPR3_U, upperBoundAddress);      /* Set the upper bound of CPU Data Protection Range 3       */
            break;
        case 4: /* Data Protection Range 4 */
            __mtcr(CPU_DPR4_L, lowerBoundAddress);      /* Set the lower bound of CPU Data Protection Range 4       */
            __mtcr(CPU_DPR4_U, upperBoundAddress);      /* Set the upper bound of CPU Data Protection Range 4       */
            break;
        case 5: /* Data Protection Range 5 */
            __mtcr(CPU_DPR5_L, lowerBoundAddress);      /* Set the lower bound of CPU Data Protection Range 5       */
            __mtcr(CPU_DPR5_U, upperBoundAddress);      /* Set the upper bound of CPU Data Protection Range 5       */
            break;
        case 6: /* Data Protection Range 6 */
            __mtcr(CPU_DPR6_L, lowerBoundAddress);      /* Set the lower bound of CPU Data Protection Range 6       */
            __mtcr(CPU_DPR6_U, upperBoundAddress);      /* Set the upper bound of CPU Data Protection Range 6       */
            break;
        case 7: /* Data Protection Range 7 */
            __mtcr(CPU_DPR7_L, lowerBoundAddress);      /* Set the lower bound of CPU Data Protection Range 7       */
            __mtcr(CPU_DPR7_U, upperBoundAddress);      /* Set the upper bound of CPU Data Protection Range 7       */
            break;
        case 8: /* Data Protection Range 8 */
            __mtcr(CPU_DPR8_L, lowerBoundAddress);      /* Set the lower bound of CPU Data Protection Range 8       */
            __mtcr(CPU_DPR8_U, upperBoundAddress);      /* Set the upper bound of CPU Data Protection Range 8       */
            break;
        case 9: /* Data Protection Range 9 */
            __mtcr(CPU_DPR9_L, lowerBoundAddress);      /* Set the lower bound of CPU Data Protection Range 9       */
            __mtcr(CPU_DPR9_U, upperBoundAddress);      /* Set the upper bound of CPU Data Protection Range 9       */
            break;
        case 10: /* Data Protection Range 10 */
            __mtcr(CPU_DPR10_L, lowerBoundAddress);     /* Set the lower bound of CPU Data Protection Range 10      */
            __mtcr(CPU_DPR10_U, upperBoundAddress);     /* Set the upper bound of CPU Data Protection Range 10      */
            break;
        case 11: /* Data Protection Range 11 */
            __mtcr(CPU_DPR11_L, lowerBoundAddress);     /* Set the lower bound of CPU Data Protection Range 11      */
            __mtcr(CPU_DPR11_U, upperBoundAddress);     /* Set the upper bound of CPU Data Protection Range 11      */
            break;
        case 12: /* Data Protection Range 12 */
            __mtcr(CPU_DPR12_L, lowerBoundAddress);     /* Set the lower bound of CPU Data Protection Range 12      */
            __mtcr(CPU_DPR12_U, upperBoundAddress);     /* Set the upper bound of CPU Data Protection Range 12      */
            break;
        case 13: /* Data Protection Range 13 */
            __mtcr(CPU_DPR13_L, lowerBoundAddress);     /* Set the lower bound of CPU Data Protection Range 13      */
            __mtcr(CPU_DPR13_U, upperBoundAddress);     /* Set the upper bound of CPU Data Protection Range 13      */
            break;
        case 14: /* Data Protection Range 14 */
            __mtcr(CPU_DPR14_L, lowerBoundAddress);     /* Set the lower bound of CPU Data Protection Range 14      */
            __mtcr(CPU_DPR14_U, upperBoundAddress);     /* Set the upper bound of CPU Data Protection Range 14      */
            break;
        case 15: /* Data Protection Range 15 */
            __mtcr(CPU_DPR15_L, lowerBoundAddress);     /* Set the lower bound of CPU Data Protection Range 15      */
            __mtcr(CPU_DPR15_U, upperBoundAddress);     /* Set the upper bound of CPU Data Protection Range 15      */
            break;
        case 16: /* Data Protection Range 15 */
            __mtcr(CPU_DPR16_L, lowerBoundAddress);     /* Set the lower bound of CPU Data Protection Range 16      */
            __mtcr(CPU_DPR16_U, upperBoundAddress);     /* Set the upper bound of CPU Data Protection Range 16      */
            break;
        case 17: /* Data Protection Range 15 */
            __mtcr(CPU_DPR17_L, lowerBoundAddress);     /* Set the lower bound of CPU Data Protection Range 17      */
            __mtcr(CPU_DPR17_U, upperBoundAddress);     /* Set the upper bound of CPU Data Protection Range 17      */
            break;
    }

#if !defined(__TASKING__)
    __isync();
#endif
}

/* Function that defines a code protection range in the corresponding CPU Code Protection Range Register (CPR).
 * Code protection ranges have 8-byte granularity.
 * As a result, the lower 3 bits of any address passed to the define_code_protection_range function will be discarded.
 * After enabling the Memory Protection, access to an address 'x' will be allowed only if:
 * lowerBoundAddress <= x < upperBoundAddress
 */
void define_code_protection_range(uint32 lowerBoundAddress, uint32 upperBoundAddress, uint8 range)
{
    switch(range)
    {
        case 0: /* Code Protection Range 0 */
            __mtcr(CPU_CPR0_L, lowerBoundAddress);      /* Set the lower bound of CPU Code Protection Range 0       */
            __mtcr(CPU_CPR0_U, upperBoundAddress);      /* Set the upper bound of CPU Code Protection Range 0       */
            break;
        case 1: /* Code Protection Range 1 */
            __mtcr(CPU_CPR1_L, lowerBoundAddress);      /* Set the lower bound of CPU Code Protection Range 1       */
            __mtcr(CPU_CPR1_U, upperBoundAddress);      /* Set the upper bound of CPU Code Protection Range 1       */
            break;
        case 2: /* Code Protection Range 2 */
            __mtcr(CPU_CPR2_L, lowerBoundAddress);      /* Set the lower bound of CPU Code Protection Range 2       */
            __mtcr(CPU_CPR2_U, upperBoundAddress);      /* Set the upper bound of CPU Code Protection Range 2       */
            break;
        case 3: /* Code Protection Range 3 */
            __mtcr(CPU_CPR3_L, lowerBoundAddress);      /* Set the lower bound of CPU Code Protection Range 3       */
            __mtcr(CPU_CPR3_U, upperBoundAddress);      /* Set the upper bound of CPU Code Protection Range 3       */
            break;
        case 4: /* Code Protection Range 4 */
            __mtcr(CPU_CPR4_L, lowerBoundAddress);      /* Set the lower bound of CPU Code Protection Range 4       */
            __mtcr(CPU_CPR4_U, upperBoundAddress);      /* Set the upper bound of CPU Code Protection Range 4       */
            break;
        case 5: /* Code Protection Range 5 */
            __mtcr(CPU_CPR5_L, lowerBoundAddress);      /* Set the lower bound of CPU Code Protection Range 5       */
            __mtcr(CPU_CPR5_U, upperBoundAddress);      /* Set the upper bound of CPU Code Protection Range 5       */
            break;
        case 6: /* Code Protection Range 6 */
            __mtcr(CPU_CPR6_L, lowerBoundAddress);      /* Set the lower bound of CPU Code Protection Range 6       */
            __mtcr(CPU_CPR6_U, upperBoundAddress);      /* Set the upper bound of CPU Code Protection Range 6       */
            break;
        case 7: /* Code Protection Range 7 */
            __mtcr(CPU_CPR7_L, lowerBoundAddress);      /* Set the lower bound of CPU Code Protection Range 7       */
            __mtcr(CPU_CPR7_U, upperBoundAddress);      /* Set the upper bound of CPU Code Protection Range 7       */
            break;
        case 8: /* Code Protection Range 7 */
            __mtcr(CPU_CPR8_L, lowerBoundAddress);      /* Set the lower bound of CPU Code Protection Range 8       */
            __mtcr(CPU_CPR8_U, upperBoundAddress);      /* Set the upper bound of CPU Code Protection Range 8       */
            break;
        case 9: /* Code Protection Range 7 */
            __mtcr(CPU_CPR9_L, lowerBoundAddress);      /* Set the lower bound of CPU Code Protection Range 9       */
            __mtcr(CPU_CPR9_U, upperBoundAddress);      /* Set the upper bound of CPU Code Protection Range 9       */
            break;
    }

#if !defined(__TASKING__)
    __isync();
#endif
}

/* Function to enable the data read access to a predefined Range in a Protection Set */
void enable_data_read(uint8 protectionSet, uint8 range)
{
    Ifx_CPU_DPRE DPRERegisterValue;

    /* Get the CPU Data Protection Read Enable Register value */
    switch(protectionSet)
    {
        case 0: /* Protection Set 0 */
            DPRERegisterValue.U = __mfcr(CPU_DPRE_0);
            break;
        case 1: /* Protection Set 1 */
            DPRERegisterValue.U = __mfcr(CPU_DPRE_1);
            break;
        case 2: /* Protection Set 2 */
            DPRERegisterValue.U = __mfcr(CPU_DPRE_2);
            break;
        case 3: /* Protection Set 3 */
            DPRERegisterValue.U = __mfcr(CPU_DPRE_3);
            break;
        case 4: /* Protection Set 4 */
            DPRERegisterValue.U = __mfcr(CPU_DPRE_4);
            break;
        case 5: /* Protection Set 5 */
            DPRERegisterValue.U = __mfcr(CPU_DPRE_5);
            break;

    }

    DPRERegisterValue.B.RE_N |= (1 << range); /* Set the bit corresponding to the given Data Protection Range */

    /* Set the CPU Data Protection Read Enable Register value to enable data read access */
    switch(protectionSet)
    {
        case 0: /* Protection Set 0 */
            __mtcr(CPU_DPRE_0, DPRERegisterValue.U);
            break;
        case 1: /* Protection Set 1 */
            __mtcr(CPU_DPRE_1, DPRERegisterValue.U);
            break;
        case 2: /* Protection Set 2 */
            __mtcr(CPU_DPRE_2, DPRERegisterValue.U);
            break;
        case 3: /* Protection Set 3 */
            __mtcr(CPU_DPRE_3, DPRERegisterValue.U);
            break;
        case 4: /* Protection Set 4 */
            __mtcr(CPU_DPRE_4, DPRERegisterValue.U);
            break;
        case 5: /* Protection Set 5 */
            __mtcr(CPU_DPRE_5, DPRERegisterValue.U);
            break;
    }

#if !defined(__TASKING__)
    __isync();
#endif
}

/* Function to enable the data write access to a predefined Range in a Protection Set */
void enable_data_write(uint8 protectionSet, uint8 range)
{
    Ifx_CPU_DPWE DPWERegisterValue;

    /* Get the CPU Data Protection Write Enable Register value */
    switch(protectionSet)
    {
        case 0: /* Protection Set 0 */
            DPWERegisterValue.U = __mfcr(CPU_DPWE_0);
            break;
        case 1: /* Protection Set 1 */
            DPWERegisterValue.U = __mfcr(CPU_DPWE_1);
            break;
        case 2: /* Protection Set 2 */
            DPWERegisterValue.U = __mfcr(CPU_DPWE_2);
            break;
        case 3: /* Protection Set 3 */
            DPWERegisterValue.U = __mfcr(CPU_DPWE_3);
            break;
        case 4: /* Protection Set 4 */
            DPWERegisterValue.U = __mfcr(CPU_DPWE_4);
            break;
        case 5: /* Protection Set 5 */
            DPWERegisterValue.U = __mfcr(CPU_DPWE_5);
            break;
    }

    /* Set the bit corresponding to the given Data Protection Range */
    DPWERegisterValue.B.WE_N |= (0x1 << range);

    /* Set the CPU Data Protection Write Enable Register value to enable data write access */
    switch(protectionSet)
    {
        case 0: /* Protection Set 0 */
            __mtcr(CPU_DPWE_0, DPWERegisterValue.U);
            break;
        case 1: /* Protection Set 1 */
            __mtcr(CPU_DPWE_1, DPWERegisterValue.U);
            break;
        case 2: /* Protection Set 2 */
            __mtcr(CPU_DPWE_2, DPWERegisterValue.U);
            break;
        case 3: /* Protection Set 3 */
            __mtcr(CPU_DPWE_3, DPWERegisterValue.U);
            break;
        case 4: /* Protection Set 4 */
            __mtcr(CPU_DPWE_4, DPWERegisterValue.U);
            break;
        case 5: /* Protection Set 5 */
            __mtcr(CPU_DPWE_5, DPWERegisterValue.U);
            break;
    }

#if !defined(__TASKING__)
    __isync();
#endif
}

/* Function to enable code execution access to a predefined Range in a Protection Set */
void enable_code_execution(uint8 protectionSet, uint8 range)
{
    Ifx_CPU_CPXE CPXERegisterValue;

    /* Get the CPU Code Protection Execute Enable Register value */
    switch(protectionSet)
    {
        case 0: /* Protection Set 0 */
            CPXERegisterValue.U = __mfcr(CPU_CPXE_0);
            break;
        case 1: /* Protection Set 1 */
            CPXERegisterValue.U = __mfcr(CPU_CPXE_1);
            break;
        case 2: /* Protection Set 2 */
            CPXERegisterValue.U = __mfcr(CPU_CPXE_2);
            break;
        case 3: /* Protection Set 3 */
            CPXERegisterValue.U = __mfcr(CPU_CPXE_3);
            break;
        case 4: /* Protection Set 4 */
            CPXERegisterValue.U = __mfcr(CPU_CPXE_4);
            break;
        case 5: /* Protection Set 5 */
            CPXERegisterValue.U = __mfcr(CPU_CPXE_5);
            break;
    }

    /* Set the bit corresponding to the given Code Protection Range */
    CPXERegisterValue.B.XE_N |= (0x1 << range);

    /* Set the CPU Code Protection Execute Enable Register value to enable coded execution */
    switch(protectionSet)
    {
        case 0: /* Protection Set 0 */
            __mtcr(CPU_CPXE_0, CPXERegisterValue.U);
            break;
        case 1: /* Protection Set 1 */
            __mtcr(CPU_CPXE_1, CPXERegisterValue.U);
            break;
        case 2: /* Protection Set 2 */
            __mtcr(CPU_CPXE_2, CPXERegisterValue.U);
            break;
        case 3: /* Protection Set 3 */
            __mtcr(CPU_CPXE_3, CPXERegisterValue.U);
            break;
        case 4: /* Protection Set 4 */
            __mtcr(CPU_CPXE_4, CPXERegisterValue.U);
            break;
        case 5: /* Protection Set 5 */
            __mtcr(CPU_CPXE_5, CPXERegisterValue.U);
            break;
    }

#if !defined(__TASKING__)
    __isync();
#endif
}
extern char __app0_data_end[];

void arch_switch_mpu(void)
{
	disable_memory_protection();
	uintptr_t task_id = current->osek_id;
	uintptr_t task_code_start, task_code_end;
	uintptr_t task_data_start, task_data_end;
	uintptr_t task_stack_start, task_stack_end;
	uintptr_t kernel_code_start, kernel_code_end;
	uintptr_t _kernel_data_start, _kernel_data_end;
	void *stack;

	stack = current_proc_info();

	task_code_start = task_address_range[task_id].code_range.start;
	task_code_end = task_address_range[task_id].code_range.end;

	task_data_start = task_address_range[task_id].data_range.start;
	task_data_end = task_address_range[task_id].data_range.end;

	task_stack_start = (uintptr_t)stack;
	task_stack_end = (uintptr_t)stack + (uintptr_t)THREAD_START_SP;

    /* 配置任务堆栈段的mpu保护 */
    define_data_protection_range((uint32)task_stack_start, task_stack_end + 1, DATA_PROTECTION_RANGE_4);
    enable_data_read(PROTECTION_SET_1, DATA_PROTECTION_RANGE_4);
    enable_data_write(PROTECTION_SET_1, DATA_PROTECTION_RANGE_4);

    define_data_protection_range(__rodata_start, __rodata_end + 1, DATA_PROTECTION_RANGE_5);
    enable_data_read(PROTECTION_SET_1, DATA_PROTECTION_RANGE_5);

    /* 配置mmu */
    /* 配置任务数据段的mpu保护 */
    define_data_protection_range(task_data_start, (uint32)task_data_end + 1, DATA_PROTECTION_RANGE_6);
    enable_data_read(PROTECTION_SET_1, DATA_PROTECTION_RANGE_6);
    enable_data_write(PROTECTION_SET_1, DATA_PROTECTION_RANGE_6);

    /* 配置任务代码段的mpu保护  */
    define_code_protection_range(task_code_start, task_code_end + 1, CODE_PROTECTION_RANGE_2);
    enable_code_execution(PROTECTION_SET_1, CODE_PROTECTION_RANGE_2);

    /* 使能mpu */
    enable_memory_protection();
}

void mpu_init()
{
    /* 配置kernel 代码段的mpu保护 */
//    define_code_protection_range(kernel_text_start, kernel_text_end + 1, CODE_PROTECTION_RANGE_0);
	define_code_protection_range(kernel_text_start, kernel_text_end + 1, CODE_PROTECTION_RANGE_0);
    enable_code_execution(PROTECTION_SET_1, CODE_PROTECTION_RANGE_0);

    /* 配置kernel 数据段的mpu保护 */
    define_data_protection_range((uint32)kernel_data_start, kernel_data_end + 1, DATA_PROTECTION_RANGE_0);
    enable_data_read(PROTECTION_SET_1, DATA_PROTECTION_RANGE_0);
    enable_data_write(PROTECTION_SET_1, DATA_PROTECTION_RANGE_0);

    define_data_protection_range(__rodata_start, __rodata_end + 1, DATA_PROTECTION_RANGE_1);
    enable_data_read(PROTECTION_SET_1, DATA_PROTECTION_RANGE_1);

    if (get_core_id() == 0) {
        define_data_protection_range(__CSA0, __CSA0_END + 1, DATA_PROTECTION_RANGE_2);
        enable_data_read(PROTECTION_SET_1, DATA_PROTECTION_RANGE_2);
        enable_data_write(PROTECTION_SET_1, DATA_PROTECTION_RANGE_2);
    } else if (get_core_id() == 1) {
        define_data_protection_range(__CSA1, __CSA1_END + 1, DATA_PROTECTION_RANGE_2);
        enable_data_read(PROTECTION_SET_1, DATA_PROTECTION_RANGE_2);
        enable_data_write(PROTECTION_SET_1, DATA_PROTECTION_RANGE_2);
    }

    define_data_protection_range(0xF0000000, 0xFFFFFFFF, DATA_PROTECTION_RANGE_3);
    enable_data_read(PROTECTION_SET_1, DATA_PROTECTION_RANGE_3);
    enable_data_write(PROTECTION_SET_1, DATA_PROTECTION_RANGE_3);

    /*
     * 在tc397中陷入trap时，如果开启了mpu，则总是默认使用PROTECTION_SET_0规定的地址保护范围，所以需要提前设置好PROTECTION_SET_0。
     */
    define_code_protection_range(FIRST_ADDR, LAST_ADDR, CODE_PROTECTION_RANGE_9);
    define_data_protection_range(FIRST_ADDR, LAST_ADDR, DATA_PROTECTION_RANGE_17);
    enable_code_execution(PROTECTION_SET_0, CODE_PROTECTION_RANGE_9);
    enable_data_read(PROTECTION_SET_0, DATA_PROTECTION_RANGE_17);
    enable_data_write(PROTECTION_SET_0, DATA_PROTECTION_RANGE_17);
}
